using System;
using System.Threading;
using System.Data;
using System.Data.Common;
using System.Collections.Generic;

namespace SunRiseSoft.Library.DataAccess
{

	public class TransactionControl : IDisposable
	{

			// To detect redundant calls
		private bool _disposed = false;

		private Dictionary<string, DbTransaction> _transactions = new Dictionary<string, DbTransaction>();

		private TransactionControlOption _transactionControlOption;

		private IsolationLevel _isolationLevel;

		private bool _completed;

		private TransactionControl _parent;

		[ThreadStatic()]
		private static TransactionControl _head;
#region "Construction"
        public TransactionControl()
            : this(TransactionControlOption.Required, IsolationLevel.Unspecified)
        {
        }

        public TransactionControl(TransactionControlOption option)
            : this(option, IsolationLevel.Unspecified)
        {
        }

        public TransactionControl(IsolationLevel iso)
            : this(TransactionControlOption.Required, iso)
        {
        }

		public TransactionControl(TransactionControlOption option, IsolationLevel iso)
		{
			_transactionControlOption = option;
			_isolationLevel = iso;
			Thread.BeginThreadAffinity();
			_parent = _head;
			_head = this;
		}
#endregion

		public void Complete()
		{
			if (_disposed) {
				throw new ObjectDisposedException("TransactionControl");
			}

			if (_transactionControlOption == TransactionControlOption.Suppress) {
				throw new InvalidOperationException("The current TransactionControlOption is Suppress.There's no transaction.");
			}

			if (_completed) {
				throw new InvalidOperationException("The current TransactionScope is already complete. You should dispose the TransactionScope.");
			}

			_completed = true;
		}

		public static DbTransaction GetTransaction(Database db)
		{
			if (_head == null) {
				return null;
			}

			TransactionControl wrapper = _head;

			switch (wrapper._transactionControlOption) {
				case TransactionControlOption.Required:
					wrapper = GetBaseWrapper(wrapper);
                    break;

				case TransactionControlOption.RequiresNew:
                    break;

				case TransactionControlOption.Suppress:
					return null;
			}

			if (!wrapper._transactions.ContainsKey(db.ConnectionString)) {
				wrapper._transactions.Add(db.ConnectionString, db.GetNewOpenConnection().BeginTransaction(wrapper._isolationLevel));
			}

			return wrapper._transactions[db.ConnectionString];
		}

		private static TransactionControl GetBaseWrapper(TransactionControl tc)
		{
			TransactionControl wrapper = tc;
			while (wrapper._parent != null) {
				wrapper = wrapper._parent;
				if (wrapper._transactionControlOption == TransactionControlOption.RequiresNew) {
					break; // TODO: might not be correct. Was : Exit Do
				}
			}
			return wrapper;
		}

		// IDisposable
		protected virtual void Dispose(bool disposing)
		{
			if (!_disposed) {
				_disposed = true;

				if (disposing) {

					//Debug.Assert(ReferenceEquals(Me, _head), "Disposed out of order")
					if (!ReferenceEquals(this, _head)) {
						throw new Exception("Disposed out of order");
					}
					_head = _parent;
					Thread.EndThreadAffinity();

					switch (_transactionControlOption) {
						case TransactionControlOption.Required:
							if (_head != null) {
								return;
							}
                            break;

						case TransactionControlOption.RequiresNew:
                            break;

						case TransactionControlOption.Suppress:
							return;

					}

					foreach (DbTransaction tran in _transactions.Values) {
						DbConnection conn = tran.Connection;
						try {
							if (_completed) {
								tran.Commit();
							}
							else {
								tran.Rollback();
							}
						}
						catch (Exception ex) {
							//Debug.WriteLine(ex)
						}
						conn.Close();
					}
				}
			}

		}
#region " IDisposable Support "
		// This code added by Visual Basic to correctly implement the disposable pattern.
		public void Dispose()
		{
			// Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
			Dispose(true);
			GC.SuppressFinalize(this);
		}

        ~TransactionControl()
		{
			// Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
			Dispose(false);
			//base.Finalize();
		}
#endregion

	}

	public enum TransactionControlOption
	{
		Required,
		RequiresNew,
		Suppress
	}
}
